# This code is heavily based on the implementation in `dbx_build_tools`:
#   Ref: https://github.com/dropbox/dbx_build_tools/blob/master/thirdparty/openssl/BUILD.openssl.tail

load("@openssl-generated-overlay//:collate_into_directory.bzl", "collate_into_directory")
load("@openssl-generated-overlay//:common.bzl", "COMMON_GENERATED_FILES")
load(
    "@openssl-generated-overlay//:constants-darwin64-arm64-cc.bzl",
    _DARWIN_ARM64_GEN_FILES = "GEN_FILES",
    _DARWIN_ARM64_LIBCRYPTO_DEFINES = "LIBCRYPTO_DEFINES",
    _DARWIN_ARM64_LIBCRYPTO_SRCS = "LIBCRYPTO_SRCS",
    _DARWIN_ARM64_LIBSSL_DEFINES = "LIBSSL_DEFINES",
    _DARWIN_ARM64_LIBSSL_SRCS = "LIBSSL_SRCS",
    _DARWIN_ARM64_OPENSSL_APP_DEFINES = "LIBSSL_DEFINES",
    _DARWIN_ARM64_OPENSSL_APP_SRCS = "OPENSSL_APP_SRCS",
    _DARWIN_ARM64_OPENSSL_DEFINES = "OPENSSL_DEFINES",
    _DARWIN_ARM64_PERLASM_GEN = "PERLASM_GEN",
    _DARWIN_ARM64_PERLASM_OUTS = "PERLASM_OUTS",
    _DARWIN_ARM64_PERLASM_TOOLS = "PERLASM_TOOLS",
)
load(
    "@openssl-generated-overlay//:constants-darwin64-x86_64-cc.bzl",
    _DARWIN_X86_64_GEN_FILES = "GEN_FILES",
    _DARWIN_X86_64_LIBCRYPTO_DEFINES = "LIBCRYPTO_DEFINES",
    _DARWIN_X86_64_LIBCRYPTO_SRCS = "LIBCRYPTO_SRCS",
    _DARWIN_X86_64_LIBSSL_DEFINES = "LIBSSL_DEFINES",
    _DARWIN_X86_64_LIBSSL_SRCS = "LIBSSL_SRCS",
    _DARWIN_X86_64_OPENSSL_APP_DEFINES = "LIBSSL_DEFINES",
    _DARWIN_X86_64_OPENSSL_APP_SRCS = "OPENSSL_APP_SRCS",
    _DARWIN_X86_64_OPENSSL_DEFINES = "OPENSSL_DEFINES",
    _DARWIN_X86_64_PERLASM_GEN = "PERLASM_GEN",
    _DARWIN_X86_64_PERLASM_TOOLS = "PERLASM_TOOLS",
)
load(
    "@openssl-generated-overlay//:constants-linux-aarch64.bzl",
    _LINUX_ARM64_GEN_FILES = "GEN_FILES",
    _LINUX_ARM64_LIBCRYPTO_DEFINES = "LIBCRYPTO_DEFINES",
    _LINUX_ARM64_LIBCRYPTO_SRCS = "LIBCRYPTO_SRCS",
    _LINUX_ARM64_LIBSSL_DEFINES = "LIBSSL_DEFINES",
    _LINUX_ARM64_LIBSSL_SRCS = "LIBSSL_SRCS",
    _LINUX_ARM64_OPENSSL_APP_DEFINES = "LIBSSL_DEFINES",
    _LINUX_ARM64_OPENSSL_APP_SRCS = "OPENSSL_APP_SRCS",
    _LINUX_ARM64_OPENSSL_DEFINES = "OPENSSL_DEFINES",
    _LINUX_ARM64_PERLASM_GEN = "PERLASM_GEN",
    _LINUX_ARM64_PERLASM_OUTS = "PERLASM_OUTS",
    _LINUX_ARM64_PERLASM_TOOLS = "PERLASM_TOOLS",
)
load(
    "@openssl-generated-overlay//:constants-linux-x86_64-clang.bzl",
    _LINUX_X86_64_GEN_FILES = "GEN_FILES",
    _LINUX_X86_64_LIBCRYPTO_DEFINES = "LIBCRYPTO_DEFINES",
    _LINUX_X86_64_LIBCRYPTO_SRCS = "LIBCRYPTO_SRCS",
    _LINUX_X86_64_LIBSSL_DEFINES = "LIBSSL_DEFINES",
    _LINUX_X86_64_LIBSSL_SRCS = "LIBSSL_SRCS",
    _LINUX_X86_64_OPENSSL_APP_DEFINES = "LIBSSL_DEFINES",
    _LINUX_X86_64_OPENSSL_APP_SRCS = "OPENSSL_APP_SRCS",
    _LINUX_X86_64_OPENSSL_DEFINES = "OPENSSL_DEFINES",
    _LINUX_X86_64_PERLASM_GEN = "PERLASM_GEN",
    _LINUX_X86_64_PERLASM_OUTS = "PERLASM_OUTS",
    _LINUX_X86_64_PERLASM_TOOLS = "PERLASM_TOOLS",
)
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
load("//:utils.bzl", "get_repo_name")

_REPO_NAME = get_repo_name()

cc_library(
    name = "crypto-textual-hdrs",
    textual_hdrs = [
        "crypto/des/ncbc_enc.c",
        "crypto/LPdir_unix.c",
    ],
)

COMMON_OPENSSL_APP_SRCS = glob(
    [
        "include/internal/*.h",
        "apps/include/*.h",
        "apps/*.h",
        "include/openssl/*.h",
    ],
    exclude = COMMON_GENERATED_FILES,
) + [
    ":apps/progs.h",
    ":include/openssl/asn1.h",
    ":include/openssl/asn1t.h",
    ":include/openssl/conf.h",
    ":include/openssl/crypto.h",
    ":include/openssl/ct.h",
    ":include/openssl/err.h",
    ":include/openssl/bio.h",
    ":include/openssl/cmp.h",
    ":include/openssl/cms.h",
    ":include/openssl/crmf.h",
    ":include/openssl/configuration.h",
    ":include/openssl/core_names.h",
    ":include/openssl/fipskey.h",
    ":include/openssl/ess.h",
    ":include/openssl/lhash.h",
    ":include/openssl/opensslconf.h",
    ":include/openssl/opensslv.h",
    ":include/openssl/ocsp.h",
    ":include/openssl/pkcs7.h",
    ":include/openssl/pkcs12.h",
    ":include/openssl/safestack.h",
    ":include/openssl/srp.h",
    ":include/openssl/ssl.h",
    ":include/openssl/ui.h",
    ":include/openssl/x509.h",
    ":include/openssl/x509v3.h",
    ":include/openssl/x509_vfy.h",
]

cc_binary(
    name = "openssl",
    srcs = COMMON_OPENSSL_APP_SRCS + select({
        "@bazel_tools//src/conditions:linux_aarch64": _LINUX_ARM64_OPENSSL_APP_SRCS,
        "@bazel_tools//src/conditions:linux_x86_64": _LINUX_X86_64_OPENSSL_APP_SRCS,
        "@bazel_tools//src/conditions:darwin_arm64": _DARWIN_ARM64_OPENSSL_APP_SRCS,
        "@bazel_tools//src/conditions:darwin_x86_64": _DARWIN_X86_64_OPENSSL_APP_SRCS,
    }),
    copts = [
        "-iquote",
        "$(BINDIR)/external/{}/apps".format(_REPO_NAME),
        "-iquote",
        "external/{}/apps".format(_REPO_NAME),
        "-iquote",
        "external/{}/apps/include".format(_REPO_NAME),
    ] + select({
        "@bazel_tools//src/conditions:linux_aarch64": _LINUX_ARM64_OPENSSL_APP_DEFINES,
        "@bazel_tools//src/conditions:linux_x86_64": _LINUX_X86_64_OPENSSL_APP_DEFINES,
        "@bazel_tools//src/conditions:darwin_arm64": _DARWIN_ARM64_OPENSSL_APP_DEFINES,
        "@bazel_tools//src/conditions:darwin_x86_64": _DARWIN_X86_64_OPENSSL_APP_DEFINES,
    }),
    visibility = ["//visibility:public"],
    deps = [":ssl"],
)

COMMON_OPENSSL_COPTS = [
    # As described in https://github.com/openssl/openssl/issues/4575, OpenSSL doesn't mark its
    # assembly files as not requiring an executable stack. Pass --noexecstack to the assembler
    # to do this.
    "-Wa,--noexecstack",
    # If someone wants to link with -fPIC, the objects they're linking need to have been built with it.
    # Add this flag so that people can choose to link that way if they want to.
    "-fPIC",
    "-Wno-unused-command-line-argument",
    "-I",
    "external/{}/include".format(_REPO_NAME),
] + [
    # This hardcoded path into the system mean we will find the system certs. Note Debian sets
    # OPENSSLDIR=/usr/lib/ssl, but /usr/lib/ssl mostly consists of symlinks into /etc/ssl. We
    # must set /etc/ssl here because some environments (e.g., YSS root filesystems) don't have
    # /usr/lib/ssl at all.
    "-DOPENSSLDIR=\\\"/etc/ssl\\\"",
    # This is basically a no-op, since we've disabled dynamic loading of engines.
    '-DENGINESDIR=\\"/usr/lib/engines-3.0\\"',
    # also basically a no-op
    "-DMODULESDIR=\\\"/dev/null\\\"",
    "-DL_ENDIAN",
    "-DOPENSSL_USE_NODELETE",
] + select({
    "@bazel_tools//src/conditions:linux_aarch64": ["-mno-outline-atomics"],
    "//conditions:default": [],
})

COMMON_LIBCRYPTO_SRCS = glob(
    [
        "crypto/**/*.h",
        "crypto/*.h",
        "include/crypto/**/*.h",
        "include/internal/*.h",
        "include/openssl/*.h",
        "providers/**/*.h",
        "providers/*.inc",
        "providers/implementations/**/*.inc",
    ],
    exclude = COMMON_GENERATED_FILES,
)

cc_library(
    name = "crypto",
    srcs = COMMON_LIBCRYPTO_SRCS + select({
        "@bazel_tools//src/conditions:linux_aarch64": _LINUX_ARM64_LIBCRYPTO_SRCS,
        "@bazel_tools//src/conditions:linux_x86_64": _LINUX_X86_64_LIBCRYPTO_SRCS,
        "@bazel_tools//src/conditions:darwin_arm64": _DARWIN_ARM64_LIBCRYPTO_SRCS,
        "@bazel_tools//src/conditions:darwin_x86_64": _DARWIN_X86_64_LIBCRYPTO_SRCS,
    }),
    hdrs = glob(
        [
            "include/openssl/*.h",
            "include/openssl/**/*.h",
            "include/crypto/*.h",
            "include/crypto/**/*.h",
        ],
    ) + [":generated-headers"],
    additional_compiler_inputs = [":generated-headers"] + glob(
        # There are some .c files that are conditionally included textually, we want them to be
        # available
        ["providers/implementations/**/*.c"],
        exclude = COMMON_GENERATED_FILES,
    ),
    copts = COMMON_OPENSSL_COPTS + [
        "-iquote",
        "external/{}/providers/implementations/macs".format(_REPO_NAME),
        "-iquote",
        "external/{}/providers/implementations/include".format(_REPO_NAME),
        "-I",
        "external/{}/providers/implementations/include".format(_REPO_NAME),
        "-iquote",
        "external/{}/providers/common/include".format(_REPO_NAME),
        "-iquote",
        "$(BINDIR)/external/{}/providers/common/include".format(_REPO_NAME),
        "-iquote",
        "$(BINDIR)/external/{}/crypto".format(_REPO_NAME),
        "-iquote",
        "external/{}/crypto".format(_REPO_NAME),
    ] + select({
        "@bazel_tools//src/conditions:linux_aarch64": _LINUX_ARM64_OPENSSL_DEFINES,
        "@bazel_tools//src/conditions:linux_x86_64": _LINUX_X86_64_OPENSSL_DEFINES,
        "@bazel_tools//src/conditions:darwin_arm64": _DARWIN_ARM64_OPENSSL_DEFINES,
        "@bazel_tools//src/conditions:darwin_x86_64": _DARWIN_X86_64_OPENSSL_DEFINES,
    }) + select({
        "@bazel_tools//src/conditions:linux_aarch64": _LINUX_ARM64_LIBCRYPTO_DEFINES,
        "@bazel_tools//src/conditions:linux_x86_64": _LINUX_X86_64_LIBCRYPTO_DEFINES,
        "@bazel_tools//src/conditions:darwin_arm64": _DARWIN_ARM64_LIBCRYPTO_DEFINES,
        "@bazel_tools//src/conditions:darwin_x86_64": _DARWIN_X86_64_LIBCRYPTO_DEFINES,
    }),
    # To make sure downstream targets add the right copts to be able to include the headers.
    includes = ["include"],
    linkopts = [
        "-lc",
        "-pthread",
    ],
    visibility = ["//visibility:public"],
    deps = [":crypto-textual-hdrs"],
)

COMMON_LIBSSL_SRCS = glob(
    ["ssl/**/*.h"],
    exclude = COMMON_GENERATED_FILES,
)

cc_library(
    name = "ssl",
    srcs = COMMON_LIBSSL_SRCS + select({
        "@bazel_tools//src/conditions:linux_aarch64": _LINUX_ARM64_LIBSSL_SRCS,
        "@bazel_tools//src/conditions:linux_x86_64": _LINUX_X86_64_LIBSSL_SRCS,
        "@bazel_tools//src/conditions:darwin_arm64": _DARWIN_ARM64_LIBSSL_SRCS,
        "@bazel_tools//src/conditions:darwin_x86_64": _DARWIN_X86_64_LIBSSL_SRCS,
    }),
    hdrs = glob(
        ["include/openssl/*.h"],
    ) + [":generated-headers"],
    additional_compiler_inputs = [":generated-files"],
    copts = COMMON_OPENSSL_COPTS + select({
        "@bazel_tools//src/conditions:linux_aarch64": _LINUX_ARM64_OPENSSL_DEFINES,
        "@bazel_tools//src/conditions:linux_x86_64": _LINUX_X86_64_OPENSSL_DEFINES,
        "@bazel_tools//src/conditions:darwin_arm64": _DARWIN_ARM64_OPENSSL_DEFINES,
        "@bazel_tools//src/conditions:darwin_x86_64": _DARWIN_X86_64_OPENSSL_DEFINES,
    }) + select({
        "@bazel_tools//src/conditions:linux_aarch64": _LINUX_ARM64_LIBSSL_DEFINES,
        "@bazel_tools//src/conditions:linux_x86_64": _LINUX_X86_64_LIBSSL_DEFINES,
        "@bazel_tools//src/conditions:darwin_arm64": _DARWIN_ARM64_LIBSSL_DEFINES,
        "@bazel_tools//src/conditions:darwin_x86_64": _DARWIN_X86_64_LIBSSL_DEFINES,
    }),
    # To make sure downstream targets add the right copts to be able to include the headers.
    includes = ["include"],
    linkopts = ["-lc"],
    visibility = ["//visibility:public"],
    deps = [
        ":crypto",
    ],
)

PERLASM_TOOLS = glob(
    ["crypto/perlasm/*.pl"],
    exclude = COMMON_GENERATED_FILES,
)

CC_ENV_CMD = """export CC=$(CC)\n"""

# Outs does not support select, so we trick a bit. Each architecture has its own
# list of files, and we use the architecture as the key.
[
    genrule(
        name = "asm_" + k,
        srcs = ["crypto/ec/ecp_nistz256_table.c"],
        outs = v,
        cmd = select({
            "@bazel_tools//src/conditions:linux_aarch64": CC_ENV_CMD + _LINUX_ARM64_PERLASM_GEN,
            "@bazel_tools//src/conditions:linux_x86_64": CC_ENV_CMD + _LINUX_X86_64_PERLASM_GEN,
            "@bazel_tools//src/conditions:darwin_arm64": CC_ENV_CMD + _DARWIN_ARM64_PERLASM_GEN,
            "@bazel_tools//src/conditions:darwin_x86_64": CC_ENV_CMD + _DARWIN_X86_64_PERLASM_GEN,
        }),
        target_compatible_with = [{
            "arm64": "@platforms//cpu:arm64",
            "x86_64": "@platforms//cpu:x86_64",
        }[k]],
        toolchains = [
            "@bazel_tools//tools/cpp:current_cc_toolchain",
            "@rules_perl//:current_toolchain",
        ],
        tools = PERLASM_TOOLS + select({
            "@bazel_tools//src/conditions:linux_aarch64": depset(direct = _LINUX_ARM64_PERLASM_TOOLS).to_list(),
            "@bazel_tools//src/conditions:linux_x86_64": depset(direct = _LINUX_X86_64_PERLASM_TOOLS).to_list(),
            "@bazel_tools//src/conditions:darwin_arm64": depset(direct = _DARWIN_ARM64_PERLASM_TOOLS).to_list(),
            "@bazel_tools//src/conditions:darwin_x86_64": depset(direct = _DARWIN_X86_64_PERLASM_TOOLS).to_list(),
        }),
    )
    for k, v in {
        "arm64": _DARWIN_ARM64_PERLASM_OUTS,
        "x86_64": _LINUX_X86_64_PERLASM_OUTS,
    }.items()
]

filegroup(
    name = "generated-files",
    srcs = COMMON_GENERATED_FILES + [":generate-platform-specific-files"],
)

filegroup(
    name = "generated-headers",
    srcs = [f for f in COMMON_GENERATED_FILES + _DARWIN_ARM64_GEN_FILES.keys() if f.endswith(".h")],
)

[
    genrule(
        name = "copy_generated_{}".format(src),
        srcs = ["@openssl-generated-overlay//:{}".format(src)],
        outs = [src],
        cmd = "cp $< $@",
    )
    for src in COMMON_GENERATED_FILES
]

GEN_FILE_CMD = """
cat << 'E_O_F' >$(location {filename})
{contents}
E_O_F
"""

genrule(
    name = "generate-platform-specific-files",
    outs = sorted(_DARWIN_ARM64_GEN_FILES.keys()),
    cmd = select({
        "@bazel_tools//src/conditions:linux_aarch64": "\n".join([GEN_FILE_CMD.format(
            contents = v,
            filename = k,
        ) for k, v in _LINUX_ARM64_GEN_FILES.items()]),
        "@bazel_tools//src/conditions:linux_x86_64": "\n".join([GEN_FILE_CMD.format(
            contents = v,
            filename = k,
        ) for k, v in _LINUX_X86_64_GEN_FILES.items()]),
        "@bazel_tools//src/conditions:darwin_arm64": "\n".join([GEN_FILE_CMD.format(
            contents = v,
            filename = k,
        ) for k, v in _DARWIN_ARM64_GEN_FILES.items()]),
        "@bazel_tools//src/conditions:darwin_x86_64": "\n".join([GEN_FILE_CMD.format(
            contents = v,
            filename = k,
        ) for k, v in _DARWIN_X86_64_GEN_FILES.items()]),
    }),
)

collate_into_directory(
    name = "gen_dir",
    outs_prefix_map = {
        ":crypto": "lib/",
        ":openssl": "bin/",
        ":ssl": "lib/",
    },
    srcs_prefix_map = {
        ":crypto": "external/{}".format(_REPO_NAME),
        ":generated-headers": "external/{}".format(_REPO_NAME),
        ":openssl": "external/{}".format(_REPO_NAME),
        ":ssl": "external/{}".format(_REPO_NAME),
    } | {
        k: "external/{}".format(_REPO_NAME)
        for k in glob(["include/openssl/*.h"])
    },
    visibility = ["//visibility:public"],
)
